﻿Option Strict On
Imports Microsoft.WindowsAPICodePack.Dialogs

Public Class frmMain

    Private Const DEFAULT_MASK As String = "*.*"    'default filter when adding files to list
    Private Const DEFAULT_EXCLUDE_NONMATCHING = True   'default status of form checkbox to include/exclude non-matching files

    Private oMasterColumnSorter As FileListViewSorter
    Private oResultColumnSorter As FileListViewSorter
    Private oMasterList As FileList
    Private oResult As FileList

    '---TO DO---    
    '   *   threading for drag/drop?
    '   *   option to move to alternate folder
    '   *   consider returning filelist of selected items only to create recycler, rather than creating fileinfo array / consider alternate way for creating recycler object from context menu click, if possible
    '   *   updating when drag/drop is used
    '   *   change textbox to multiline, validate entry to prevent carriage returns
    '   *   possible to make a method for ifilelistindex?
    '   *   clicking update will not reload from disk (so if changes are made to file structure app won't be aware) - look into changing this behaviour
    '   *   error handling for drag/drop
    '   *   path goes blank on cancel
    '-----------

    ''' <summary>
    ''' Adds a menu item to the specified ContextMenu.
    ''' </summary>
    ''' <param name="menu">ContextMenu to which to add the item.</param>
    ''' <param name="caption">The caption of the menu item to add.</param>
    Private Sub addContextMenuItem(ByVal menu As ContextMenu, ByVal caption As String)

        Dim oMenuItem As New MenuItem(caption)

        menu.MenuItems.Add(oMenuItem)
        AddHandler oMenuItem.Click, AddressOf eventContextMenuItemClick

    End Sub

    ''' <summary>
    ''' Adds each instance of a unique file extension in the specified FileList to the nominated ComboBox.
    ''' </summary>
    ''' <param name="comboBox">ComboBox to which to add the items.</param>
    ''' <param name="contents">FileList containing the files from which to extract the file extensions.</param>
    ''' <param name="includeWildCardMask">Add a wildcard file mask to the top of the list.</param>
    Private Sub addComboBoxItems(ByVal comboBox As ComboBox, ByVal contents As FileList, Optional ByVal includeWildCardMask As Boolean = False)

        Dim oExtensions As ArrayList = contents.GetFileExtensions()

        With comboBox
            .Items.Clear()
            If includeWildCardMask Then .Items.Add(DEFAULT_MASK)

            If oExtensions IsNot Nothing Then
                For i As Integer = oExtensions.Count - 1 To 0 Step -1
                    .Items.Add(oExtensions.Item(i).ToString)
                Next
            End If

            RemoveHandler .SelectedIndexChanged, AddressOf eventFileMaskChanged
            .SelectedIndex = 0
            AddHandler .SelectedIndexChanged, AddressOf eventFileMaskChanged

        End With

    End Sub

    ''' <summary>
    ''' Adds the contents of a FileList to the nominated ListView without raising ItemCheck events.
    ''' </summary>
    ''' <param name="listView">ListView to which to add the items.</param>
    ''' <param name="contents">FileList containing the files to add to the ListView,</param>
    ''' <param name="fileMask">File mask by which to filter added items (format: *.xxx)</param>
    Private Sub addListViewItems(ByVal listView As ListView, ByVal contents As FileList, Optional ByVal fileMask As String = DEFAULT_MASK, Optional ByVal returnHiddenFiles As Boolean = False)

        listView.Items.Clear()
        RemoveHandler listView.ItemCheck, AddressOf eventItemCheck
        listView.Items.AddRange(contents.GetListViewItems(fileMask, returnHiddenFiles))
        AddHandler listView.ItemCheck, AddressOf eventItemCheck
        listView.Tag = contents

    End Sub

    ''' <summary>
    ''' Displays a folder browser dialog and returns the chosen path.
    ''' </summary>
    ''' <param name="displayText">Text to display in dialog titlebar.</param>
    ''' <param name="startingPath">Initial directory to display in browser.</param>
    ''' <returns>Full path of directory chosen by user, empty string if cancelled.</returns>
    ''' <remarks>Currently only works for Windows Vista and above.  Exception will be raised on XP due to the use of the WindowsCodePack dialog.</remarks>
    Private Function browseForPath(ByVal displayText As String, Optional ByVal startingPath As String = vbNullString) As String

        Dim oFolderBrowser As New CommonOpenFileDialog(displayText)

        With oFolderBrowser
            .IsFolderPicker = True

            If startingPath Is Nothing Then
                .InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)   'start at the desktop if no root folder specified
            Else
                .InitialDirectory = startingPath    'no need for validation; will default to last valid location if invalid
            End If

            If .ShowDialog.Equals(CommonFileDialogResult.Ok) Then Return .FileName Else Return String.Empty 'return null if cancelled
        End With

    End Function

    ''' <summary>
    ''' Handler for btnBrowseMaster.Click event.  Launches a folder browser for the oMasterList FileList path.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub btnBrowseMaster_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBrowseMaster.Click

        Dim sPath As String = browseForPath(String.Format(My.Resources.PromptBrowseSource, My.Resources.GenericStringMaster), boxMasterPath.Text)
        If sPath Is Nothing Then Exit Sub
        boxMasterPath.Text = sPath
        populateMasterList(sPath)
        If oResult IsNot Nothing Then populateResultList(True) 'populate the result list if it already exists

    End Sub

    ''' <summary>
    ''' Handler for btnBrowseOrphan.Click event.  Launches a folder browser for the oResult FileList path.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub btnBrowseSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBrowseOrphan.Click

        Dim sPath As String = browseForPath(String.Format(My.Resources.PromptBrowseSource, My.Resources.GenericStringOrphan), boxMasterPath.Text) 'open browser at same location as master
        If sPath Is Nothing Then Exit Sub
        boxOrphanPath.Text = sPath
        If oMasterList IsNot Nothing Then populateResultList()

    End Sub

    ''' <summary>
    ''' Handler for btnDelete.Click event.  Deletes or recycles checked and displayed files in oResult FileList using new FileRecycler instance.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click

        Dim oRecycler As New FileRecycler(oResult)
        If oRecycler.PerformFileOperation(FileRecycler.OperationMode.MoveToRecycleBin) Then populateResultList() '*** TEST FUNCTIONALITY WITH DRAG/DROP

    End Sub

    ''' <summary>
    ''' Handler for btnMove.Click event.  Prompts for destination path and moves checked and displayed files in oResult FileList using new FileRecycler instance.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub btnMove_Click(sender As System.Object, e As System.EventArgs) Handles btnMove.Click

        Dim oRecycler As New FileRecycler(oResult)
        If oRecycler.PerformFileOperation(FileRecycler.OperationMode.MoveToPath, browseForPath(My.Resources.PromptBrowseDestination)) Then populateResultList() '*** TEST FUNCTIONALITY WITH DRAG/DROP

    End Sub

    ''' <summary>
    ''' Handler for btnUpdate.Click event.  Updates ListView controls.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub btnUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpdate.Click

        populateResultList()

    End Sub

    ''' <summary>
    ''' Creates a new instance of a FileList object and handles any errors thrown.
    ''' </summary>
    ''' <param name="fileList">Reference to the FileList object to instantiate.</param>
    ''' <param name="path">Path of directory containing files from which to create FileList.</param>
    ''' <param name="fileMask">File mask by which to filter files (format: *.xxx).</param>
    ''' <param name="params">Optional parameters to be applied to all files.</param>
    ''' <returns>True if FileList was created successfully, otherwise false.</returns>
    Private Function createFileList(ByRef fileList As FileList, ByVal path As String, ByVal fileMask As String, ByVal params As FileList.ItemParamaters) As Boolean

        Dim sErrorSource As String

        If fileList Is oMasterList Then sErrorSource = My.Resources.GenericStringMaster Else sErrorSource = My.Resources.GenericStringSearch

        Try
            fileList = New FileList(path, fileMask, params)
        Catch ex As Exception
            If Not FileErrorHandler.HandleFileError(ex, sErrorSource) Then   'attempt to handle error
                Me.Close()  'close if unsuccessful
            End If
            Return False    'list was not created successfully
        End Try

        Return True 'list was created successfully

    End Function

    ''' <summary>
    ''' Displays a context menu on the specified control at the mouse click event coordinates.
    ''' </summary>
    ''' <param name="listView">The ListView on which to display the menu.</param>
    ''' <param name="e">MouseEventArgs raised by the MouseUp event.</param>
    Private Sub displayContextMenu(ByVal listView As ListView, ByVal e As System.Windows.Forms.MouseEventArgs)

        Dim oContextMenu As ContextMenu = New ContextMenu()
        oContextMenu.Tag = listView

        If listView.SelectedItems.Count > 0 Then
            addContextMenuItem(oContextMenu, My.Resources.MenuLabelOpen)
            addContextMenuItem(oContextMenu, My.Resources.MenuLabelDelete)
            addContextMenuItem(oContextMenu, My.Resources.CharMenuSeparator)
            addContextMenuItem(oContextMenu, My.Resources.MenuLabelOpenLocation)
            addContextMenuItem(oContextMenu, My.Resources.CharMenuSeparator)
            addContextMenuItem(oContextMenu, My.Resources.MenuLabelCheck)
            addContextMenuItem(oContextMenu, My.Resources.MenuLabelUncheck)
        End If

        oContextMenu.Show(listView, New Point(e.X, e.Y))

    End Sub

    ''' <summary>
    ''' Handler for ListView.ColumnClick events.  Performs checkbox select/deselect all and column sorting.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles lstFilesMaster.ColumnClick, lstFilesResult.ColumnClick

        Dim oParentListView As ListView = DirectCast(sender, ListView)
        Dim oParentListViewColumnSorter As FileListViewSorter
        Dim oParentFileList As FileList = DirectCast(oParentListView.Tag, FileList)

        If oParentFileList IsNot Nothing Then

            Select Case e.Column
                Case 0  'checkbox column - perform select/deselect all

                    RemoveHandler oParentListView.ItemCheck, AddressOf eventItemCheck
                    Dim newSelectAllStatus As Boolean = Not oParentFileList.SelectAll

                    For i As Integer = 0 To oParentListView.Items.Count - 1
                        oParentListView.Items.Item(i).Checked = newSelectAllStatus
                    Next

                    AddHandler oParentListView.ItemCheck, AddressOf eventItemCheck

                    oParentFileList.SelectAll = newSelectAllStatus
                    populateResultList(True)
                Case Else   'perform column sort

                    If oParentListView Is lstFilesMaster Then oParentListViewColumnSorter = oMasterColumnSorter Else oParentListViewColumnSorter = oResultColumnSorter

                    If (e.Column = oParentListViewColumnSorter.SortColumn) Then 'determine if clicked column is the column being sorted

                        If (oParentListViewColumnSorter.Order = SortOrder.Ascending) Then   'reverse the current sort direction for the column
                            oParentListViewColumnSorter.Order = SortOrder.Descending
                        Else
                            oParentListViewColumnSorter.Order = SortOrder.Ascending
                        End If
                    Else
                        oParentListViewColumnSorter.SortColumn = e.Column   'set the column number to be sorted
                        oParentListViewColumnSorter.Order = SortOrder.Ascending     'default to ascending order
                    End If

                    oParentListView.Sort()
            End Select
        End If

    End Sub

    ''' <summary>
    ''' Handler for MenuItem.Click events.  Performs open/delete/check options for individual or multiple ListView items.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventContextMenuItemClick(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim oMenuItemClicked As MenuItem = DirectCast(sender, MenuItem)
        Dim oParentListView As ListView = DirectCast(oMenuItemClicked.GetContextMenu.Tag, ListView)
        Dim oParentFileList As FileList = DirectCast(oParentListView.Tag, FileList)

        Select Case oMenuItemClicked.Text
            Case My.Resources.MenuLabelDelete
                Dim iCurrentIndex As Integer = 0
                Dim iFileCount As Integer = oParentListView.SelectedIndices.Count - 1
                Dim iFileListIndex As Integer
                Dim oFilesToDelete(iFileCount) As IO.FileInfo

                For Each selection In oParentListView.SelectedIndices
                    iFileListIndex = CInt(oParentListView.Items.Item(CInt(selection)).Tag)
                    oFilesToDelete(iCurrentIndex) = New IO.FileInfo(oParentFileList.ListItemPath(iFileListIndex) & My.Resources.CharPathDelimiter & oParentFileList.ListItemFilename(iFileListIndex))      'create an array of FileInfo objects for each selected item
                    iCurrentIndex += 1
                Next


                Using oRecycler As New FileRecycler(New FileList(oFilesToDelete, FileList.ItemParamaters.CheckedAndDisplayed))        'create a new FileRecycler instance using a new FileList object from the previously created FileInfo array
                    If oRecycler.PerformFileOperation(FileRecycler.OperationMode.MoveToRecycleBin) Then
                        If oParentListView Is lstFilesMaster Then populateMasterList(oParentFileList.ParentDirectory) 'repopulate the master list to reflect the deleted items
                        populateResultList()     're-calculate the results based on the updated master list
                    End If
                End Using

            Case My.Resources.MenuLabelOpen, My.Resources.MenuLabelOpenLocation

                Dim oDirectories As New ArrayList
                Dim openLocation As Boolean = False

                If oMenuItemClicked.Text.Equals(My.Resources.MenuLabelOpenLocation) Then openLocation = True

                For Each selection In oParentListView.SelectedIndices
                    Dim iFileListIndex As Integer = CInt(oParentListView.Items.Item(CInt(selection)).Tag)   'index of current item

                    If openLocation Then
                        Dim sDirectory As String = oParentFileList.ListItemPath(iFileListIndex)    'path of current item

                        If Not oDirectories.Contains(sDirectory) Then   'check if path has already featured in previous items so only one instance of each folder is opened for multiple selections in the same directory
                            oDirectories.Add(sDirectory)
                            oParentFileList.OpenFile(iFileListIndex, openLocation)  'path is unique, open location
                        End If
                    Else
                        oParentFileList.OpenFile(iFileListIndex, openLocation)  'open file 
                    End If
                Next

            Case My.Resources.MenuLabelCheck, My.Resources.MenuLabelUncheck

                Dim checkStatus As Boolean = True
                If oMenuItemClicked.Text.Equals(My.Resources.MenuLabelUncheck) Then checkStatus = False

                RemoveHandler oParentListView.ItemCheck, AddressOf eventItemCheck   'remove itemcheck handler to prevent event being raised when item.checked called

                For Each selection In oParentListView.SelectedIndices
                    Dim iListViewIndex As Integer = CInt(selection)     'index of current item in ListView control
                    Dim iFileListIndex As Integer = CInt(oParentListView.Items.Item(iListViewIndex).Tag)   'index of current item in FileList object

                    oParentFileList.IsChecked(iFileListIndex) = checkStatus
                    oParentListView.Items.Item(iListViewIndex).Checked = checkStatus
                Next

                AddHandler oParentListView.ItemCheck, AddressOf eventItemCheck

                updateStatus()
        End Select

    End Sub

    ''' <summary>
    ''' Handler for boxMasterPath.DragDrop and lstFilesMaster.DragDrop events.  Performs lstFilesMaster ListView population on item drag/drop.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventDragDropMaster(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles boxMasterPath.DragDrop, lstFilesMaster.DragDrop

        oMasterList = New FileList(e, DEFAULT_MASK, FileList.ItemParamaters.CheckedAndDisplayed)
        addListViewItems(lstFilesMaster, oMasterList)
        boxMasterPath.Text = oMasterList.ParentDirectory
        addComboBoxItems(cmbMasterExt, oMasterList, True)
        My.Settings.lastMasterPath = oMasterList.ParentDirectory    'save last path setting

    End Sub

    ''' <summary>
    ''' Handler for boxOrphanPath.DragDrop and lstFilesResult.DragDrop events.  Performs lstFilesResult ListView population on item drag/drop.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventDragDropResult(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles boxOrphanPath.DragDrop, lstFilesResult.DragDrop

        oResult = New FileList(e, DEFAULT_MASK, FileList.ItemParamaters.Displayed)
        boxOrphanPath.Text = oResult.ParentDirectory
        addComboBoxItems(cmbOrphanExt, oResult)
        If oMasterList IsNot Nothing Then populateResultList(True)
        My.Settings.lastSearchPath = oResult.ParentDirectory    'save last path setting

    End Sub

    ''' <summary>
    ''' Handler for ComboBox.SelectedIndexChanged events.  Updates ListViews when file mask is changed.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventFileMaskChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkExcludeNonMatchingFiles.CheckedChanged, cmbMasterExt.SelectedIndexChanged, cmbOrphanExt.SelectedIndexChanged

        If sender Is cmbMasterExt Then addListViewItems(lstFilesMaster, oMasterList, cmbMasterExt.SelectedItem.ToString, True) 'repopulate the main list if the main list mask was changed
        populateResultList(True)

    End Sub

    ''' <summary>
    ''' Handler for ListView.ItemCheck events.  Checks and unchecks ListView items.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventItemCheck(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles lstFilesMaster.ItemCheck, lstFilesResult.ItemCheck

        If (ModifierKeys And (Keys.Shift Or Keys.Control)) <> 0 Then    'this is required to work around a bug with checkboxes and multiselect in the listview control
            e.NewValue = e.CurrentValue
            Exit Sub
        End If

        Dim oSenderListView As ListView = DirectCast(sender, ListView)
        Dim oParentFileList As FileList = DirectCast(oSenderListView.Tag, FileList)    'filelist belonging to listview that raised the event   
        Dim iFileListIndex As Integer = CInt(oSenderListView.Items.Item(e.Index).Tag)  'index of the item within the filelist that was checked

        oParentFileList.IsChecked(iFileListIndex) = Not oParentFileList.IsChecked(iFileListIndex)
        If Not sender Is lstFilesResult AndAlso oResult IsNot Nothing Then populateResultList(True) 'update result list in real time; ignore if event was raised by result list, or result list not populated
        updateStatus()

    End Sub

    ''' <summary>
    ''' Handler for DragEnter events.  Enables drag/drop when item is dragged onto a valid control.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventItemDragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles boxMasterPath.DragEnter, boxOrphanPath.DragEnter, lstFilesMaster.DragEnter, lstFilesResult.DragEnter

        If e.Data.GetDataPresent(DataFormats.FileDrop) Then e.Effect = DragDropEffects.All

    End Sub

    ''' <summary>
    ''' Handler for ListView.MouseUp events.  Displays a context menu on ListView right-click.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventListViewMouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lstFilesMaster.MouseUp, lstFilesResult.MouseUp

        If e.Button.Equals(MouseButtons.Right) Then displayContextMenu(DirectCast(sender, ListView), e)

    End Sub

    ''' <summary>
    ''' Handler for TextBox.KeyPress events.  Populates ListView when enter is pressed within a corresponding TextBox control.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub eventTextBoxKeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles boxMasterPath.KeyPress, boxOrphanPath.KeyPress

        If e.KeyChar.Equals(ChrW(Keys.Enter)) Then
            If sender Is boxMasterPath Then populateMasterList(boxMasterPath.Text)
            If sender Is boxOrphanPath Then populateResultList()
        End If

    End Sub

    ''' <summary>
    ''' Handler for FormClosed event.  Saves last used directory paths.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub frmMain_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed

        My.Settings.Save()  'save last successfully used file path

    End Sub

    ''' <summary>
    ''' Handler for MyBase.Load event.  Sets initial status for controls and populates last used paths.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        btnMove.Enabled = False 'disable move/delete buttons before listviews are populated
        btnDelete.Enabled = False
        chkExcludeNonMatchingFiles.Checked = DEFAULT_EXCLUDE_NONMATCHING

        boxMasterPath.Text = My.Settings.lastMasterPath 'load last path setting
        boxOrphanPath.Text = My.Settings.lastSearchPath

        lblDetail.Text = My.Resources.FormLabelDetailInitial
        lblStatus.Text = String.Format(My.Resources.FormLabelMainStatus, 0, 0, FileList.FormatFileSize(0, FileList.DefaultFileUnit)) 'set default status display

        oMasterColumnSorter = New FileListViewSorter(1, SortOrder.Ascending)  'create and assign listview column sorters
        oResultColumnSorter = New FileListViewSorter(1, SortOrder.Ascending)
        lstFilesMaster.ListViewItemSorter = oMasterColumnSorter
        lstFilesResult.ListViewItemSorter = oResultColumnSorter

    End Sub

    ''' <summary>
    ''' Handler for Resize event.  Resizes controls that are unable to be resized correctly using the anchor property.
    ''' </summary>
    ''' <param name="sender">Event sender object.</param>
    ''' <param name="e">Event arguments object.</param>
    Private Sub frmMain_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        lstFilesMaster.Height = boxOrphanPath.Location.Y - 80   'anchor bottom of lstFilesMaster to top of boxOrphanPath
        lstFilesResult.Location = New Point(lstFilesResult.Location.X, boxOrphanPath.Location.Y + 26) 'anchor top of lstFilesResult to top of boxOrphanPath
        lstFilesResult.Height = lstFilesMaster.Height   'set lstFilesResult height to height of lstFilesMaster

    End Sub

    ''' <summary>
    ''' Instantiates the oMasterList FileList object and uses this to populate the lstFilesMaster ListView control.
    ''' </summary>
    ''' <param name="path">Path of directory containing files from which to create FileList.</param>
    ''' <param name="fileMask">File mask by which to filter files (format: *.xxx).</param>
    ''' <returns>True if ListView was populated successfully, otherwise false.</returns>
    Private Function populateMasterList(ByVal path As String, Optional ByVal fileMask As String = DEFAULT_MASK) As Boolean

        If String.IsNullOrEmpty(path) OrElse Not createFileList(oMasterList, path, fileMask, FileList.ItemParamaters.CheckedAndDisplayed) Then Return False

        addListViewItems(lstFilesMaster, oMasterList, , True)
        addComboBoxItems(cmbMasterExt, oMasterList, True)
        My.Settings.lastMasterPath = path   'save last path setting
        Return True 'list population successful

    End Function

    ''' <summary>
    ''' Instantiates new oResult FileList object and uses this to populate the lstFilesResult ListView control.  Populates lstFilesMaster ListView if not already performed.
    ''' </summary>
    ''' <param name="retainExistingList">Optional switch to use existing instance of oResult FileList.</param>
    Private Sub populateResultList(Optional ByVal retainExistingList As Boolean = False)

        If oMasterList Is Nothing Then  'master list hasn't been populated yet
            If Not populateMasterList(Me.boxMasterPath.Text) Then Exit Sub 'list wasn't created successfully; exit to prevent multiple error messages being shown
        End If

        If Not retainExistingList Then    'reload the result list from disk
            If String.IsNullOrEmpty(Me.boxOrphanPath.Text) Then Exit Sub 'if the search box is changed between masks being changed, this will trigger - should behaviour be changed so path is only changed on update/enter/browse etc?
            If Not createFileList(oResult, boxOrphanPath.Text, DEFAULT_MASK, FileList.ItemParamaters.Displayed) Then Exit Sub
            addComboBoxItems(cmbOrphanExt, oResult)
        End If

        Dim sMask = cmbOrphanExt.SelectedItem.ToString
        oResult.HideOrphanedFiles(oMasterList, sMask)
        My.Settings.lastSearchPath = Me.boxOrphanPath.Text  'save last path setting

        If Not chkExcludeNonMatchingFiles.Checked Then sMask = My.Resources.CharWildcard

        addListViewItems(lstFilesResult, oResult, sMask)
        updateStatus()
        If oResult.FileCount(FileList.ItemParamaters.Displayed) <= 0 Then MessageBox.Show(My.Resources.OutcomeNoOrphansFound, My.Resources.DialogTitleAlert, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

    End Sub

    ''' <summary>
    ''' Updates form status text and enables/disbales btnDelete and btnMove based on the number of checked files in lstFilesResult ListView.
    ''' </summary>
    Private Sub updateStatus()

        If oResult Is Nothing Then Exit Sub 'prevent error being thrown when result is empty

        Dim buttonsEnabled As Boolean
        Dim iCheckedFiles As Integer = oResult.FileCount(FileList.ItemParamaters.CheckedAndDisplayed)
        Dim sMasterExt As String = FileList.StripFileMask(cmbMasterExt.SelectedItem.ToString)
        Dim sOrphanedExt As String = FileList.StripFileMask(cmbOrphanExt.SelectedItem.ToString).ToUpper

        If sMasterExt.Equals(My.Resources.CharWildcard) Then sMasterExt = My.Resources.CharSpace Else sMasterExt = My.Resources.CharExtensionDelimiter & sMasterExt.ToUpper & My.Resources.CharSpace
        lblStatus.Text = String.Format(My.Resources.FormLabelMainStatus, (oResult.FileCount(FileList.ItemParamaters.Displayed)), iCheckedFiles, FileList.FormatFileSize(oResult.GetTotalSize(FileList.ItemParamaters.CheckedAndDisplayed), FileList.DefaultFileUnit))
        lblDetail.Text = String.Format(My.Resources.FormLabelDetail, sOrphanedExt, sMasterExt)

        If iCheckedFiles > 0 Then buttonsEnabled = True Else buttonsEnabled = False ' only enable the move/delete button if one or more items are checked
        btnDelete.Enabled = buttonsEnabled
        btnMove.Enabled = buttonsEnabled

    End Sub

End Class
